; NOTE:
; ANY symbol entry should be marked as GLOBAL if the preceding instruction
; is not a RET so that the Code Compressor can determine a call to the
; entry is really a call here and not say a wraparound case
;
  .area text

; Inputs: R19..R16 = 32-bit signed dividend
;	  Y+3..Y+0 = 32-bit signed divisor
; Output: R19..R16 = 32-bit quotient/remainder

div32u::
	clt
	rjmp	divmod_u
mod32u::
	set
divmod_u:
	rcall	long_div_prolog
	clr	R12		; Never any final sign correction
	rjmp	divmod_aux
div32s::
	clt
	rjmp	div_mod
mod32s::
	set
div_mod:
	rcall	long_div_prolog	; save R8-R12, R24-R27, R30; divisor to R24..R27
	sbrc	r19,7
	rcall	neg_R16_R19	; make dividend >= 0
	sbrc	r27,7
	rcall	neg_R24_R27	; make divisor >= 0
divmod_aux:
	clr	R7		; zero-extend dividend to 40 bits (for unsigned ops)
	clr	r8		; and another 32 bits for the remainder
	clr	r9
	clr	r10
	clr	r11
	rcall	tst_R16_R19
	breq	alldone		; dividend == 0
	rcall	tst_R24_R27
	breq	alldone		; divisor == 0
	ldi	r30,40
loop:	lsl	r16
	rol	r17
	rol	r18
	rol	r19
	rol	r7
	rol	r8
	rol	r9
	rol	r10
	rol	r11
	cp	r8,r24
	cpc	r9,r25
	cpc	r10,r26
	cpc	r11,r27
	brcs	bitdone		; branch if not big enough to subtract
	sub	r8,r24
	sbc	r9,r25
	sbc	r10,r26
	sbc	r11,r27
	inc	r16
bitdone:
	dec	r30
	brne	loop
alldone:
	brtc	.L10		; branch if dividing
	mov	r16,r8		; move remainder to return value
	mov	r17,r9
	mov	r18,r10
	mov	r19,r11
.L10:	rjmp	long_div_epilog

long_div_prolog::
  st    -y,r7
  st    -y,r8
  st    -y,r9
  st    -y,r10
  st    -y,r11
  st    -y,r12
  st    -y,r30
  st    -y,r24
  st    -y,r25
  st    -y,r26
  st    -y,r27
  ldd   r24,y+11	; load divisor
  ldd   r25,y+12
  ldd   r26,y+13
  ldd   r27,y+14
  mov	R12,R19		; result sign (mod)
  brts	pro_exit
  eor	R12,R27		; result sign (div)
pro_exit:
  ret

long_div_epilog::
  sbrc  r12,7
  rcall	neg_R16_R19	; final sign correction
  ld    r27,y+
  ld    r26,y+
  ld    r25,y+
  ld    r24,y+
  ld    r30,y+
  ld    r12,y+
  ld    r11,y+
  ld    r10,y+
  ld    r9,y+
  ld    r8,y+
  ld    r7,y+
  adiw  r28,4		; delete divisor
  ret

tst_R16_R19::
  mov	r30,r16
  or    r30,r17
  or    r30,r18
  or    r30,r19
  ret

tst_R24_R27::
  mov	r30,r24
  or    r30,r25
  or    r30,r26
  or    r30,r27
  ret

neg_R16_R19::
  COM	R16
  COM	R17
  COM	R18
  COM	R19
  SUBI	R16,0xFF
  SBCI	R17,0xFF
  SBCI	R18,0xFF
  SBCI	R19,0xFF
  RET

neg_R24_R27::
  COM	R24
  COM	R25
  COM	R26
  COM	R27
  SUBI	R24,0xFF
  SBCI	R25,0xFF
  SBCI	R26,0xFF
  SBCI	R27,0xFF
  RET
